; Over Horizon - The Arcade Game
; ------------------------------

; This is a patch file for the tool bincmp.

; The purpose of this file is to create a ROM hack of the NES game "Over Horizon".
; The fictitous backstory for the hack is that "Over Horizon" was originally an arcade game.
; And this arcade game later got an NES port with extended contents.
; (Similar to "Rush'n Attack" whose NES version has a new soundtrack, two new levels and a different plot.)
; Our hack restores the game to its arcade form, or rather to an NES game that's a straight port.
; In reality, I like this game very much since it's a shoot'em up that is not unforgivingly hard.
; But the last two stages just drag on too much and especially the stage 5 boss is very frustrating and unfair.
; That's why I decided to cut the game down to a more digestable experience.

; The ROM analysis, the technical work of finding out how the game functions, was done by kandowontu.
; The design choices (which levels to cut, what other alterations to do) were decided by me, Denny.

; Source NES ROM file: "Over Horizon (J).nes"
; Intended output file name: "Over Horizon - The Arcade Game (J) [h].nes"

; Usage:
; - Copy the original ROM and rename it accordingly.
; - On the command line, execute the following:
;   bincmp "Over Horizon - The Arcade Game (J) [h].nes" -patch "Over Horizon - The Arcade Game (J) [h].nes patch.txt"


; Cheats for testing
; ------------------

; For testing purposes, there are two useful cheats:

; Level skip:
; Hold Right + A + B on controller 2 while pressing Reset.
; During gameplay, press A on controller 2 and Select on controller 1.

; Infinite lives:
; Game Genie code SZVUSSVK.


; Level IDs
; ---------

; Every sub level of a stage, including bosses, as well as every menu screen has its own level ID.
; The current level ID gets saved into RAM address $0074.

; $00: Stage 1
; $01: Stage 2
; $02: Stage 4
; $03: Stage 3
; $04: Stage 5 start
; $05: Stage 6 start, outside 1
; $06: Stage 6 after sub boss
; $07: Stage 6 outside 2
; $08: Stage 6 single screen before sub boss
; $09: Title screen
; $0A: Option editor
; $0B: Stage 4 boss
; $0C: Stage 1 boss
; $0D: Stage 2 boss
; $0E: Stage 6 sub boss
; $0F: Stage 3 boss
; $10: Stage 5 boss
; $11: Stage 6 boss
; $12: ($06) Stage 6 after outside 2
; $13: Hot-B logo
; $14: Test mode
; $15: Weapon edit bar
; $16: Menu
; $17: Score bonus screen
; $18: Weapon edit bar
; $19: ($04) Stage 5 after sub boss
; $1A: Post-credit scene with alien baby
; $1B: Credits with ship artwork. Sequel hook text
; $1C: ?
; $1D: ($07) Stage 6 outside 2 at middle teleporter
; $1E: Post-victory scene with ship flying away from alien's remains
; $1F: ($08) Stage 6 before boss

; There is no level ID for the stage 5 sub boss.

; The level IDs that have an additional value in brackets are hard-coded special cases:
; If they are loaded, they immediately change the level ID to the value in the brackets.
; And then they load that level not at the start, but at a specific scrolling position offset.


; Level array
; -----------

; The order of the levels and bosses is stored in two redundant, almost identical arrays.
; The current level array index gets saved into RAM address $0076.

; For the hack, we cut out stage 5 (level ID $04 and $10) completely:
; With the sub boss in the middle, the gameplay drags on for much too long.
; And the final boss is incredibly unfair, especially after such a long level.

; In stage 6, we do the following:
; - Keep the start level, outside 1 ($05).
; - Remove the single screen before the sub boss ($08).
;   Because an empty, non-scrolling screen isn't very arcade-like.
; - Remove the sub boss ($0E).
;   Because it's barely a challenge.
;   And like the sub boss in stage 5, it lets the stage drag on too much.
; - Keep the level after the sub boss ($06).
; - Remove the outside 2 level ($07).
;   Because since we remove the next level as well, the teleporter in the middle makes no sense anymore.
; - Remove the level after the outside 2 level ($12/$06).
;   Because it has two teleporters of which one sends you back to the outside 2 level.
;   And a backward teleporter isn't very arcade-like.
; - Keep the level before the boss ($1F/$08).
; - Keep the boss ($11).

; In stage 6, the level IDs after the initial one are mostly set by the teleporters.
; So, some of the values inside the level array have no purpose during regular gameplay.
; But they are still used for the level skip cheat.
; Since we cut out some levels, we can put all of our stage 6 levels into the array.
; The original array only has some of the stage 6 levels while others cannot be accessed by the level skip cheat.
; Coincidentally, our kept levels are all among the ones that were already inside the level array anyway.

; The original value $08 at address $0001759B is an error.
; It has to be $1F, which would then turn into $08 with a scrolling offset.
; This would send you to the last level before the boss.
; $08 itself is just the single screen before the sub boss.
; But the incorrect value produces no buggy behavior because it is never read.
; Accessing that level is done by the final teleporter, not by the level array.
; And the final teleporter has the correct value $1F.
; Likewise, for the level skip cheat, the other level array is used.
; And there, the value is correctly set too ($1F at $0001DAC8).
; So, we just keep the original wrong value.

; Every value from $80 to $FF serves as the array end indicator.
; So, the fact that one array uses $FE and the other one uses $FF isn't a problem either.

; Array from which the levels, but not the bosses, in regular gameplay are read.
'0001758E:  00  00  ; Index $00
'0001758F:  0C  0C  ; Index $01
'00017590:  01  01  ; Index $02
'00017591:  0D  0D  ; Index $03
'00017592:  03  03  ; Index $04
'00017593:  0F  0F  ; Index $05
'00017594:  02  02  ; Index $06
'00017595:  0B  0B  ; Index $07
00017596:  04  05  ; Index $08
00017597:  10  06  ; Index $09
00017598:  05  08  ; Index $0A
00017599:  06  11  ; Index $0B
0001759A:  07  FE  ; Index $0C, with the new end indicator
'0001759B:  08  08  ; Index $0D
'0001759C:  11  11  ; Index $0E
'0001759D:  FE  FE  ; Index $0F

; Array from which the bosses in regular gameplay are read.
; And for the levels and bosses when the level skip cheat is used.
'0001DABB:  00  00  ; Index $00
'0001DABC:  0C  0C  ; Index $01
'0001DABD:  01  01  ; Index $02
'0001DABE:  0D  0D  ; Index $03
'0001DABF:  03  03  ; Index $04
'0001DAC0:  0F  0F  ; Index $05
'0001DAC1:  02  02  ; Index $06
'0001DAC2:  0B  0B  ; Index $07
0001DAC3:  04  05  ; Index $08
0001DAC4:  10  06  ; Index $09
0001DAC5:  05  1F  ; Index $0A
0001DAC6:  06  11  ; Index $0B
0001DAC7:  07  FF  ; Index $0C, with the new end indicator
'0001DAC8:  1F  1F  ; Index $0D
'0001DAC9:  11  11  ; Index $0E
'0001DACA:  FF  FF  ; Index $0F


; Stage 6 teleporters
; -------------------

; Every teleporter has its own destination level ID to send you to the next level.
; And the teleporter also sets the level array index with a defined value.
; Many of these level array indices don't do anything.
; Because the next level is set by the next teleporter again and not by the level array.
; But there might be two exceptions:

; A possible exception is the teleporter in the single screen before the sub boss.
; Its value might be important because the sub boss itself doesn't end with a teleporter.
; Therefore, after the sub boss is defeated, the level array index might be used.
; In reality, it turns out that the current level array index is indeed incremented to get to the correct array entry.
; Hence, the previous teleporter would have to prepare the index accordingly.
; But the sub boss also sets a hard-coded index value regardless.
; So, in the end, the teleporter index is still not used.
; But the absolute index that the sub boss sets would potentially have to be adjusted to match the correct array position.
; None of this concerns us though, since we cut both, the single screen and the sub boss anyway.

; A definite, important exception is the last teleporter:
; It has to set the level array index to one entry before the boss ID.
; Because the last level before the boss uses the regular method of:
; "Increment level array index and read level array value at new index to load boss."
; In the original array, the boss ID ($11) is stored at array index $0E.
; Hence, the last teleporter sets the index to $0E - 1 = $0D.
; Since we shifted the values around, the last teleporter's index has to match the new level array contents accordingly.
; Also, since we cut out levels, not only does the index have to be different.
; But the last teleporter itself is even a different one now.

; The teleporter also saves the x and y position of the player's spaceship.
; It saves this position for the level that the teleporter leads to.
; So, if a teleporter leads to a different level now, its x and y value has to be taken from the teleporter that originally lead there.
; This also goes for levels that originally weren't accessed by a teleporter.
; Like the level after the sub boss ($06).

; Teleporter at start, outside 1, level ID $05.
; We skip the single screen and the sub boss.
; The level array index is of no use.
; The player position for level $06 happens to be the the same as for $08.
0001DC36:  08  06  ; Level ID
'0001DC37:  0D  0D  ; Level array index
'0001DC38:  20  20  ; x position
'0001DC39:  C0  C0  ; y position

; Teleporter at single screen before sub boss, level ID $08.
; This level and the teleporter don't exist anymore.
'0001DC64:  0E  0E  ; Level ID
'0001DC65:  0A  0A  ; Level array index
'0001DC66:  20  20  ; x position
'0001DC67:  70  70  ; y position

; Teleporter at after sub boss, level ID $06.
; We skip the outside 2 and after outside 2 level.
; The level array index gets set to one entry before the boss ID.
0001DC40:  07  1F  ; Level ID
0001DC41:  0C  0A  ; Level array index
0001DC42:  50  12  ; x position
0001DC43:  20  B0  ; y position

; Teleporter at outside 2, level ID $07.
; This level and the teleporter don't exist anymore.
'0001DC5A:  12  12  ; Level ID
'0001DC5B:  0B  0B  ; Level array index
'0001DC5C:  20  20  ; x position
'0001DC5D:  40  40  ; y position

; Teleporter at after outside 2, level ID $12/$06, bottom (backward).
; This level and the teleporter don't exist anymore.
'0001DC50:  1D  1D  ; Level ID
'0001DC51:  0C  0C  ; Level array index
'0001DC52:  40  40  ; x position
'0001DC53:  30  30  ; y position

; Teleporter at after outside 2, level ID $12/$06, top (forward).
; This level and the teleporter don't exist anymore.
'0001DC48:  1F  1F  ; Level ID
'0001DC49:  0D  0D  ; Level array index
'0001DC4A:  12  12  ; x position
'0001DC4B:  B0  B0  ; y position


; Continue array
; --------------

; If you get a game over and continue, you are sent to the start of the current stage.
; There is an array that defines for every single level to which level you get sent back.
; For example, for stage 2 start, the continue point is stage 2 start.
; And for the stage 2 boss, the continue point is also stage 2 start.
; The order in the continue array is identical to the level IDs.
; I.e. the level ID gets used to calculate the continue array's index.
; For each level, there are two values stored right after another:
; - The continue level ID.
; - The continue level array index.
; These values get saved in RAM address $0075 (ID) and $0077 (array index).

; $00: Stage 1
'0001D64A:  00  00  ; Level ID
'0001D64B:  00  00  ; Level array index
; $01: Stage 2
'0001D64C:  01  01  ; Level ID
'0001D64D:  02  02  ; Level array index
; $02: Stage 4
'0001D64E:  02  02  ; Level ID
'0001D64F:  06  06  ; Level array index
; $03: Stage 3
'0001D650:  03  03  ; Level ID
'0001D651:  04  04  ; Level array index
; $04: Stage 5 start
; This level doesn't exist anymore.
'0001D652:  04  04  ; Level ID
'0001D653:  08  08  ; Level array index
; $05: Stage 6 start, outside 1
'0001D654:  05  05  ; Level ID
0001D655:  0A  08  ; Level array index
; $06: Stage 6 after sub boss
'0001D656:  05  05  ; Level ID
0001D657:  0A  08  ; Level array index
; $07: Stage 6 outside 2
; This level doesn't exist anymore.
'0001D658:  05  05  ; Level ID
'0001D659:  0A  0A  ; Level array index
; $08: Stage 6 single screen before sub boss
; This level doesn't exist anymore.
; ($1F: ($08) Stage 6 before boss)
'0001D65A:  05  05  ; Level ID
0001D65B:  0A  08  ; Level array index
; ($09: Title screen)
'0001D65C:  00  00
'0001D65D:  00  00
; ($0A: Option editor)
'0001D65E:  00  00
'0001D65F:  00  00
; $0B: Stage 4 boss
'0001D660:  02  02  ; Level ID
'0001D661:  06  06  ; Level array index
; $0C: Stage 1 boss
'0001D662:  00  00  ; Level ID
'0001D663:  00  00  ; Level array index
; $0D: Stage 2 boss
'0001D664:  01  01  ; Level ID
'0001D665:  02  02  ; Level array index
; $0E: Stage 6 sub boss
; This level doesn't exist anymore.
'0001D666:  05  05  ; Level ID
'0001D667:  0A  0A  ; Level array index
; $0F: Stage 3 boss
'0001D668:  03  03  ; Level ID
'0001D669:  04  04  ; Level array index
; $10: Stage 5 boss
; This level doesn't exist anymore.
'0001D66A:  04  04  ; Level ID
'0001D66B:  08  08  ; Level array index
; $11: Stage 6 boss
'0001D66C:  05  05  ; Level ID
0001D66D:  0A  08  ; Level array index


; Stage number tiles
; ------------------

; The stage number tiles for the intro screen of every stage.
; They are in the same order as the IDs for the main level per stage:
; $00: Stage 1
; $01: Stage 2
; $02: Stage 4
; $03: Stage 3
; $04: Stage 5 start
; $05: Stage 6 start, outside 1
; Note that stage 3 and 4 have swapped IDs.
; The same swap is also present in the tiles.
; Since we cut out stage 5, we rename stage 6 as stage 5.

'0000BC68:  50  50  ; Stage 1
'0000BC69:  42  42  ; Stage 2
'0000BC6A:  44  44  ; Stage 4 (not 3)
'0000BC6B:  52  52  ; Stage 3 (not 4)
'0000BC6C:  54  54  ; Stage 5, doesn't exist anymore
0000BC6D:  46  54  ; Original: Stage 6. Hack: Stage 5


; Title screen options
; --------------------

; We remove the edit mode since it's not very arcade-like.
; And all those configurations cannot be changed between levels.
; After the start of the game, they are fixed.
; Unless you get a game over.
; Hence, story-wise, there's only one canon configuration anyway.
; And that one is obviously the default configuration.

; The wrap-around value for cycling through the options.
; Since the edit mode is the last option, we simply decrement the wrap-around value.
; Now, only game start and continue can be selected.
00003970:  03  02

; The title screen is made up of meta tiles instead of individual tiles.
; This means we cannot just put the name of our hack somewhere.
; But we have to remove the edit mode menu text anyway.
; So, we put a shortened name of the hack inside the menu texts.

; The title screen has three lines of text.
; They are stored right after another, with the value $01 as a separator.
; So, fortunately, the lines don't have hard-coded start positions.
; This means we can shift around the length of each individual text line.
; We only have to make sure that the total length stays the same.

; Original: [GAME START#CONTINUE#EDIT MODE]
; Hack:     [ ARCADE  #GAME START#CONTINUE]
00003B57:  9D  FF  ; Original: G. Hack: _
'00003B58:  EE  EE  ; Original: A. Hack: A
00003B59:  AD  BD  ; Original: M. Hack: R
00003B5A:  9C  8E  ; Original: E. Hack: C
00003B5B:  FF  EE  ; Original: _. Hack: A
00003B5C:  BE  8F  ; Original: S. Hack: D
00003B5D:  BF  9C  ; Original: T. Hack: E
00003B5E:  EE  FF  ; Original: A. Hack: _
00003B5F:  BD  FF  ; Original: R. Hack: _
00003B60:  BF  01  ; Original: T. Hack: #
00003B61:  01  9D  ; Original: #. Hack: G
00003B62:  8E  EE  ; Original: C. Hack: A
00003B63:  AF  AD  ; Original: O. Hack: M
00003B64:  AE  9C  ; Original: N. Hack: E
00003B65:  BF  FF  ; Original: T. Hack: _
00003B66:  9F  BE  ; Original: I. Hack: S
00003B67:  AE  BF  ; Original: N. Hack: T
00003B68:  CC  EE  ; Original: U. Hack: A
00003B69:  9C  BD  ; Original: E. Hack: R
00003B6A:  01  BF  ; Original: #. Hack: T
00003B6B:  9C  01  ; Original: E. Hack: #
00003B6C:  8F  8E  ; Original: D. Hack: C
00003B6D:  9F  AF  ; Original: I. Hack: O
00003B6E:  BF  AE  ; Original: T. Hack: N
00003B6F:  FF  BF  ; Original: _. Hack: T
00003B70:  AD  9F  ; Original: M. Hack: I
00003B71:  AF  AE  ; Original: O. Hack: N
00003B72:  8F  CC  ; Original: D. Hack: U
'00003B73:  9C  9C  ; Original: E. Hack: E
'00003B74:  01  01  ; Original: #. Hack: #

; The new cursor position after the menu text shift.
000039A8:  8C  9C


; Speed selection removal
; -----------------------

; The speed selection is a pretty stupidly-implemented feature:
; You never need speed 1 or 2.
; There are no passages in the game that require such a fine control.
; But the slow speeds can severely hinder you.
; And speed 5 is only really required in stage 5 in the water, which we cut out.
; Speed 4 is useless as well.
; So, most of the time, you use the default speed of 3 anyway.
; And if you went faster, going back to the default speed is stupid button mashing:
; You have to go from 5 to 4, 3, 2, 1, 2, 3.
; Because if you just go from 5 to 3, then next time you press the button, your ship gets slower.
; But next time, you most likely want to get faster again.
; So, you better make sure that your last speed change was from 2 to 3, not from 4 to 3.
; Which means you have to press the button six times to get back to the default.
; In conclusion, we simply remove the speed selection completely.
; Let's play this arcade game with two buttons only.

; Code for speed change with sound effect.
; It gets turned into NOP ($EA).
0001DE2F:  20  EA  ; Original: JSR
0001DE30:  3F  EA  ; Original: $DE3F
0001DE31:  DE  EA  ;
0001DE32:  A9  EA  ; Original: LDA
0001DE33:  13  EA  ; Original: #$13
0001DE34:  20  EA  ; Original: JSR
0001DE35:  78  EA  ; Original: $F378
0001DE36:  F3  EA  ;


; Post-credit scene removal
; -------------------------

; We remove the post-credit scene where an alien baby appears and says it will be back.
; That was mostly a personal decision by me to have a more definite ending.
; Also, that scene appears 30 seconds after the final score screen.
; It's a nice easter egg when you leave the console on for that long.
; Especially since you don't expect it.
; But it doesn't really fit an arcade game.

; Start of the code for the post-credit scene.
; It gets turned into an infinite loop.
; This mirrors the infinite loop at the end of the original version.
000077EC:  A9  4C  ; Original: LDA   Hack: JMP
000077ED:  0A  DC  ; Original: #$0A  Hack: $B7DC
000077EE:  20  B7  ; Original: JSR